package component.security.encrypt;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.security.*;
import javax.crypto.*;

import component.security.util.Constant;
import component.security.util.FileUtil;

/**
 * 信息加密解密类
 */

public class Crypto {

  // 对称加密算法表
  public final static String DES = "DES";
  public final static String DESede = "DESede";
  public final static String TRI_DES = "TripleDES";
  public final static String AES = "AES";
  public final static String BLOWFISH = "Blowfish";

  // 公开加密算法表
  public final static String RSA = "RSA";
  public final static String RSA_ECB_PKCS1PADDING = "RSA/ECB/PKCS1Padding";

  public Crypto() {
  }

  /**
   * 使用指定密钥加密
   * @param source 源信息字节数组
   * @param key 密钥(可以是公开密钥也可以是对称密钥)
   * @return 加密后的字节数组
   * @throws NoSuchAlgorithmException 指定密钥的加密算法无效
   * @throws InvalidKeyException 指定密钥无效
   * @throws NoSuchPaddingException 指定的填充方式无效
   * @throws IllegalBlockSizeException 字节数组长度不符合数据块大小
   * @throws BadPaddingException 错误的填充方式
   * @throws NoSuchProviderException 指定的Provider不存在
   */
  public byte[] encrypt(byte[] source, Key key) throws
      NoSuchAlgorithmException, InvalidKeyException,
      NoSuchPaddingException, IllegalBlockSizeException,
      BadPaddingException, NoSuchProviderException {
    return crypt(Cipher.ENCRYPT_MODE, source, null, key);
  }
  
  /**
   * 对原文文件进行加密后，形成密文文件
   * @param key
   * @param originalFile
   * @param cryptoFile
   * @return
   * @throws NoSuchAlgorithmException
   * @throws InvalidKeyException
   * @throws NoSuchPaddingException
   * @throws IllegalBlockSizeException
   * @throws BadPaddingException
   * @throws NoSuchProviderException
 * @throws IOException 
   */
  public void encrypt(Key key, String originalFile, String lienceOrignFile,String cryptoFile)throws
	  NoSuchAlgorithmException, InvalidKeyException,
	  NoSuchPaddingException, IllegalBlockSizeException,
	  BadPaddingException, NoSuchProviderException, IOException {
		FileOutputStream fos = new FileOutputStream(new File(cryptoFile)); 
		
		BufferedReader read = new BufferedReader(new FileReader(originalFile));
		StringBuffer buffer = new StringBuffer();
		String line;
		while(( line = read.readLine() ) != null){
			buffer.append(line).append(Constant.ROWSPLIT_STRING);
		}
		byte[] result = encrypt(buffer.toString().getBytes(),key);
		fos.write(result);
		FileUtil.saveObject2File(buffer, lienceOrignFile);
		fos.close();
		read.close();	
  }
  
 

  /**
   * 使用指定密钥和指定加密参数进行加密
   * @param source 源信息字节数组
   * @param transformation 加密参数(加密算法/加密方式/填充方式)
   * @param key 密钥(可以是公开密钥也可以是对称密钥)
   * @return 加密后的字节数组
   * @throws NoSuchAlgorithmException 指定密钥的加密算法无效
   * @throws InvalidKeyException 指定密钥无效
   * @throws NoSuchPaddingException 指定的填充方式无效
   * @throws IllegalBlockSizeException 字节数组长度不符合数据块大小
   * @throws BadPaddingException 错误的填充方式
   * @throws NoSuchProviderException 指定的Provider不存在
   */
  public byte[] encrypt(byte[] source, String transformation, Key key) throws
      NoSuchAlgorithmException, InvalidKeyException,
      NoSuchPaddingException, IllegalBlockSizeException,
      BadPaddingException, NoSuchProviderException {
    return crypt(Cipher.ENCRYPT_MODE, source, transformation, key);
  }

  /**
   * 对原文文件进行解密后，形成明文文件
   * @param key
   * @param originalFile
   * @param cryptoFile
   * @return
   * @throws NoSuchAlgorithmException
   * @throws InvalidKeyException
   * @throws NoSuchPaddingException
   * @throws IllegalBlockSizeException
   * @throws BadPaddingException
   * @throws NoSuchProviderException
 * @throws IOException 
 * @throws ClassNotFoundException 
   */
  public String decrypt(Key key, String cryptoFile)throws
	  NoSuchAlgorithmException, InvalidKeyException,
	  NoSuchPaddingException, IllegalBlockSizeException,
	  BadPaddingException, NoSuchProviderException, IOException, ClassNotFoundException {
//		Object obj = Util.readObjectFromFile(cryptoFile);
		FileInputStream fis = new FileInputStream(new File(cryptoFile)); 
		byte[] crypto = new byte[1024];
		fis.read(crypto);
		fis.close();	
		byte[] result = decrypt(crypto,key);
		return new String(new String(result).getBytes(),"UTF-8");
	
		
  }
  /**
   * 使用指定密钥解密
   * @param source 信息字节数组
   * @param key 密钥(可以是公开密钥也可以是对称密钥)
   * @return 解密后的字节数组
   * @throws NoSuchAlgorithmException 指定密钥的加密算法无效
   * @throws InvalidKeyException 指定密钥无效
   * @throws NoSuchPaddingException 指定的填充方式无效
   * @throws IllegalBlockSizeException 字节数组长度不符合数据块大小
   * @throws BadPaddingException 错误的填充方式
   * @throws NoSuchProviderException 指定的Provider不存在
   */
  public byte[] decrypt(byte[] source, Key key) throws
      NoSuchAlgorithmException, InvalidKeyException,
      NoSuchPaddingException, IllegalBlockSizeException,
      BadPaddingException, NoSuchProviderException {
    return crypt(Cipher.DECRYPT_MODE, source, null, key);
  }

  /**
   * 使用指定密钥和指定参数进行解密
   * @param source 信息字节数组
   * @param transformation 加密参数(加密算法/加密方式/填充方式)
   * @param key 密钥(可以是公开密钥也可以是对称密钥)
   * @return 解密后的字节数组
   * @throws NoSuchAlgorithmException 指定密钥的加密算法无效
   * @throws InvalidKeyException 指定密钥无效
   * @throws NoSuchPaddingException 指定的填充方式无效
   * @throws IllegalBlockSizeException 字节数组长度不符合数据块大小
   * @throws BadPaddingException 错误的填充方式
   * @throws NoSuchProviderException 指定的Provider不存在
   */
  public byte[] decrypt(byte[] source, String transformation, Key key) throws
      NoSuchAlgorithmException, InvalidKeyException,
      NoSuchPaddingException, IllegalBlockSizeException,
      BadPaddingException, NoSuchProviderException {	  
    return crypt(Cipher.DECRYPT_MODE, source, transformation, key);
  }

  /**
   * 对信息进行加、解密
   * DES,DESede,Blowfish,HmacMD5,HmacSHA1
   * @param opmode 操作方式(ENCRYPT_MODE, DECRYPT_MODE, WRAP_MODE,UNWRAP_MODE)
   * @param source 要操作的字节数组
   * @param key 加解密使用的密钥
   * @return 加解密操作后产生的字节数组
   * @throws NoSuchAlgorithmException 指定的加密算法有误或不被支持
   * @throws InvalidKeyException 指定的密钥有误
   * @throws NoSuchPaddingException 数据填充方式不被支持
   * @throws IllegalBlockSizeException 数据块大小非法
   * @throws BadPaddingException 数据填充方式有误
   * @throws NoSuchProviderException 指定的Provider不存在
   */
  public byte[] crypt(int opmode, byte[] source, Key key) throws
      NoSuchAlgorithmException, InvalidKeyException,
      NoSuchPaddingException, IllegalBlockSizeException,
      BadPaddingException, NoSuchProviderException {
    return crypt(opmode, source, null, key);
  }

  /**
   * 对信息进行加、解密
   * DES,DESede,Blowfish,HmacMD5,HmacSHA1
   * @param opmode 操作方式(ENCRYPT_MODE, DECRYPT_MODE, WRAP_MODE,UNWRAP_MODE)
   * @param source 要操作的字节数组
   * @param transformation 数据填充方式
   * @param key 加解密使用的密钥
   * @return 加解密操作后产生的字节数组
   * @throws NoSuchAlgorithmException 指定的加密算法有误或不被支持
   * @throws InvalidKeyException 指定的密钥有误
   * @throws NoSuchPaddingException 数据填充方式不被支持
   * @throws IllegalBlockSizeException 数据块大小非法
   * @throws BadPaddingException 数据填充方式有误
   * @throws NoSuchProviderException 指定的Provider不存在
   */
  public byte[] crypt(int opmode, byte[] source, String transformation, Key key) throws
      NoSuchAlgorithmException, InvalidKeyException,
      NoSuchPaddingException, IllegalBlockSizeException,
      BadPaddingException, NoSuchProviderException {
    String alg = transformation != null ? transformation : key.getAlgorithm();
    byte[] dest = null;
    if (alg != null && alg.startsWith("RSA")) {
      dest = RSACrypt(alg, opmode, source, key);
    }
    else {
      Cipher cipher = Cipher.getInstance(alg, "BC");
      cipher.init(opmode, key);
      dest = cipher.doFinal(source);
    }
    return dest;
  }

  /**
   * RSA加密解密
   * @param alg RSA加密算法名称(RSA/加密方式/数据填充方式)
   * @param opmode 操作方式
   * @param source 操作的字节数组
   * @param key RSA密钥(公钥或私钥)
   * @return 加密解密结果数组
   * @throws NoSuchAlgorithmException 指定的加密算法有误或不被支持
   * @throws InvalidKeyException 指定的密钥有误
   * @throws NoSuchPaddingException 数据填充方式不被支持
   * @throws IllegalBlockSizeException 数据块大小非法
   * @throws BadPaddingException 数据填充方式有误
   * @throws NoSuchProviderException 指定的Provider不存在
   */
  private byte[] RSACrypt(String alg, int opmode, byte[] source, Key key) throws
      NoSuchAlgorithmException, InvalidKeyException,
      NoSuchPaddingException, IllegalBlockSizeException,
      BadPaddingException, NoSuchProviderException {

	  Cipher cipher = Cipher.getInstance(alg,
              new org.bouncycastle.jce.provider.BouncyCastleProvider());
	    cipher.init(opmode, key);

	    // 获取数据块大小(字节长度)
	    int blockSize = cipher.getBlockSize();
	    // 计算数据块数目(字节数组分块)
	    int blockCount = (source.length + blockSize - 1) / blockSize;
	    // 数据块缓存数组
	    ByteArray[] blocks = new ByteArray[blockCount];
	    // 计算最后一块字节长度
	    int lastSize = (source.length % blockSize == 0) ?
	                   blockSize :
	                   source.length % blockSize;

	    // 初始化各个数据块
	    for (int i = 0; i < blockCount; i ++){
	       blocks[i] = new ByteArray();
	       // 如果是最后一块则设置长度为lastSize,否则为blockSize
	       blocks[i].data = new byte[(i < blockCount - 1)?blockSize:lastSize];
	       for (int j = 0 ; j < blocks[i].data.length && (i * blockSize + j) < source.length; j++)
	         blocks[i].data[j] = source[i * blockSize + j];
	    }

	    // 分别对各块数据进行加解密运算
	    int totalLen = 0; // 输出长度
	    for (int i = 0; i < blocks.length; i ++){
	      blocks[i].data = cipher.doFinal(blocks[i].data);
	      totalLen = totalLen + blocks[i].data.length;
	    }

	    // 合并运算后的字节数组
	    byte[] output = new byte[totalLen];
	    int index = 0;
	    for (int i = 0; i < blocks.length; i ++){
	      for (int j = 0; j < blocks[i].data.length; j ++){
	        output[index] = blocks[i].data[j];
	        index ++;
	      }
	    }
	    return output;
  }

  

  class ByteArray{
    public byte[] data;
  }

}